﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Windows.Forms;
using DataAccessLayer;
using DataAccessLayer.DAO;
using Util;

namespace foobartender.forms
{
    public partial class InvoicePrint : Form
    {
        private readonly Invoice invoice;
        private readonly int invoiceId;
        private readonly List<double> cellWidths = new List<double> { 3.7, 28.7, 6.7, 6.7, 11.6, 12.5, 4.3, 12, 13.8 };
        private readonly List<bool> cellAlign = new List<bool> { true, false, true, false, true, true, true, true, true };
        private readonly Margins margins = new Margins(50, 50, 50, 50);
        private static readonly StringFormat LEFT_ALIGN = new StringFormat { Alignment = StringAlignment.Near };
        private static readonly StringFormat CENTER_ALIGN = new StringFormat { Alignment = StringAlignment.Center };
        private static readonly StringFormat RIGHT_ALIGN = new StringFormat { Alignment = StringAlignment.Far };

        private bool LandscapeOrientation
        {
            get
            {
                return pdInvoice.DefaultPageSettings.Landscape;
            }
            set
            {
                pdInvoice.DefaultPageSettings.Landscape = value;
            }
        }

        private InvoicePrint()
        {
            InitializeComponent();
            SetupComponent();
        }

        public InvoicePrint(int invoiceId)
            : this()
        {
            this.invoiceId = invoiceId;
            invoice = new Invoice(invoiceId);
        }

        private void SetupComponent()
        {
            pdInvoice.DefaultPageSettings.Margins = margins;
            pdInvoice.DefaultPageSettings.Landscape = true;
            pdInvoice.DocumentName = "Invoice#" + invoiceId;
            pdInvoice.PrintPage += PrintPage;
            printPreview.Document = pdInvoice;
        }

        private void PrintPage(object sender, PrintPageEventArgs e)
        {
            var y = e.MarginBounds.Top;
            y += PrintSellerInfo(e, new Company(Properties.Settings.Default.OwnCompanyId), y);
            y += PrintRule(e, y);
            y += Math.Max(PrintHeader(e, y), PrintBuyerInfo(e, new Company(invoice["CompanyId"].ToInt()), y));
            y += PrintInvoiceItemsTable(e, y);
            y += PrintProductFooter(e, y);
            e.Graphics.DrawString("Generated by Foo-bartender 0.4 on " + DateTime.Now, new Font(FontFamily.GenericMonospace, 6), new SolidBrush(Color.Beige), e.MarginBounds.Left, y);
        }

        private static int PrintBuyerInfo(PrintPageEventArgs e, Company c, int yStart)
        {
            var r = GetPrintArea(e);
            r.X = (r.X + r.Width) / 2;
            var g = e.Graphics;
            var y = yStart;
            y += PrintStyledLine("Buyer:", "DETAIL", g, r.X, y);
            y += PrintStyledLine("S.C. " + c["Name"], "H1", g, r.X, y);
            y += PrintStyledLine("Nr.Ord.Reg: " + c["NrOrdReg"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("CIF: " + c["CIF"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Address: " + c["Address"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("  " + c["PostalCode"] + " " + c["City"] + ", jud." + c["County"] + ", " + c["CountryName"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Telephone: " + c["Telephone"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Fax: " + c["Fax"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("E-mail: " + c["Email"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Site: " + c["Website"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Bank: " + c["BankName"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("IBAN: " + c["IBAN"], "DETAILS", g, r.X, y);
            return y - yStart;
        }

        private int PrintSellerInfo(PrintPageEventArgs e, Company c, int yStart)
        {
            var r = GetPrintArea(e);
            var g = e.Graphics;
            var y = yStart;
            y += PrintStyledLine("S.C. " + c["Name"], "SELLER", g, r.X, y);
            y += PrintStyledLine("Nr.Ord.Reg: " + c["NrOrdReg"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("CIF: " + c["CIF"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Address: " + c["Address"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("  " + c["PostalCode"] + " " + c["City"] + ", jud." + c["County"] + ", " + c["CountryName"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Telephone: " + c["Telephone"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Fax: " + c["Fax"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("E-mail: " + c["Email"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Site: " + c["Website"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Bank: " + c["BankName"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("IBAN: " + c["IBAN"], "DETAILS", g, r.X, y);
            try
            {
                var i = Image.FromFile("logo.png");
                var w = 500;
                var h = y - yStart;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                var bounds = GetResizeBounds(w, h, i.Width, i.Height);
                w = bounds.Width;
                h = bounds.Height;
                g.DrawImage(i, new Rectangle(r.X + r.Width - w, yStart, w, h), new Rectangle(0, 0, i.Width, i.Height), GraphicsUnit.Pixel);
            }
            catch (Exception ex)
            {
                Logger.Instance.Exception(this, ex);
            }
            return y - yStart;
        }

        private static Size GetResizeBounds(int dw, int dh, int sw, int sh)
        {
            var b = new Size();
            var nPercentW = (float)dw / sw;
            var nPercentH = (float)dh / sh;
            var nPercent = nPercentH < nPercentW ? nPercentH : nPercentW;
            b.Width = (int)(sw * nPercent);
            b.Height = (int)(sh * nPercent);
            return b;
        }

        private int PrintInvoiceItemsTable(PrintPageEventArgs e, int yStart)
        {
            var r = GetPrintArea(e);
            var g = e.Graphics;
            var y = (float)yStart;
            var count = 0;
            y += PrintProductLine(e, (int)y, new[] { "#", "Name", "Quant.", "Unit", "UnitPrice", "Value", "VAT%", "VAT", "Total" }, true);
            foreach (DataRow item in invoice.Items)
            {
                //if (linesPerPage < count) Logger.Instance.Log("Page overflow");Generated by Foo-bartender 0.4 on 6/18/2010 7:17:36 PM
                var p = new Product(new DbValue(item["ProductId"]).ToInt());
                var info = new[]
                               {
                                   (count+1).ToString(),
                                   p["Name"].ToString(),
                                   Converter.ReFormat(item["Quantity"]),
                                   p["UnitName"].ToString(),
                                   p["Price"].ToString(),
                                   Converter.ReFormat(item["Value"]),
                                   Properties.Settings.Default.VatRate,
                                   Converter.ReFormat(item["Vat"]),
                                   Converter.ToString(Converter.ToDouble(item["Vat"]) + Converter.ToDouble(item["Value"]))
                               };
                y += PrintProductLine(e, (int)y, info, false);
                count++;
            }
            g.DrawRectangle(new Pen(Color.Black), r.X, yStart, r.Width, y - yStart);
            var w = 0.0;
            for (var i = 0; i < cellWidths.Count - 1; ++i)
            {
                w += cellWidths[i];
                var x = r.X + (int)(w * r.Width / 100);
                g.DrawLine(new Pen(Color.Black), x, yStart, x, y);
            }
            //e.HasMorePages = false;
            return (int)y - yStart;
        }

        private int PrintProductLine(PrintPageEventArgs e, int yStart, string[] text, bool header)
        {
            var y = yStart;
            var r = GetPrintArea(e);
            var font = new Font("Lucida Console", 7.5F);
            var brush = new SolidBrush(Color.Black);
            var g = e.Graphics;
            if (header)
            {
                font = new Font(font.FontFamily, 8F, FontStyle.Bold);
            }
            else
            {
                e.Graphics.DrawLine(new Pen(brush), r.X, y, r.X + r.Width, y);
            }
            y += 3;
            var wp = 0.0;
            var k = 0;
            foreach (var s in text)
            {
                var x = r.X + (int)(wp * r.Width / 100);
                wp += cellWidths[k];
                var w = (int)(cellWidths[k] * r.Width / 100);
                var align = header ? CENTER_ALIGN : (cellAlign[k] ? RIGHT_ALIGN : LEFT_ALIGN);
                g.DrawString(s, font, brush, new RectangleF(x, y, w, font.Height), align);
                k++;
            }
            y += font.Height;
            y += 3;
            return y - yStart;
        }

        private int PrintProductFooter(PrintPageEventArgs e, int yStart)
        {
            var y = yStart;
            var r = GetPrintArea(e);
            var g = e.Graphics;
            var font = new Font("Lucida Console", 9F, FontStyle.Bold);
            var brush = new SolidBrush(Color.Black);
            var wp = 0.0;
            for (var i = 0; i < 5; ++i)
            {
                wp += cellWidths[i];
            }
            var fcw = (int)(wp * r.Width / 100);
            g.DrawString("Total to be paid for this invoice", font, brush, new RectangleF(r.X, y + 2, fcw, font.Height + 2), CENTER_ALIGN);
            g.DrawString("Total value of this invoice - incl. VAT ", font, brush, new RectangleF(r.X, y + 6 + font.Height, fcw, font.Height + 2), CENTER_ALIGN);
            var k = 5;
            foreach (var s in new[] { invoice["SumNet"].ToString(), Properties.Settings.Default.VatRate, invoice["SumVat"].ToString(), Converter.ToString(Converter.ToDouble(invoice["SumVat"]) + Converter.ToDouble(invoice["SumNet"])) })
            {
                var x = r.X + (int)(wp * r.Width / 100);
                wp += cellWidths[k];
                var w = (int)(cellWidths[k] * r.Width / 100);
                g.DrawString(s, font, brush, new RectangleF(x, y + 2, w, font.Height + 2), RIGHT_ALIGN);
                g.DrawLine(new Pen(brush), x, y, x, y + 4 + font.Height);
                k++;
            }
            y += (4 + font.Height);
            g.DrawLine(new Pen(brush), r.X, y, r.X + r.Width, y);
            g.DrawString(
                Converter.ToString(Converter.ToDouble(invoice["SumVat"]) + Converter.ToDouble(invoice["SumNet"])), font,
                brush, new Rectangle(r.X + r.Width - 200, y + 2, 200, font.Height + 2), RIGHT_ALIGN);
            y += (4 + font.Height);

            g.DrawRectangle(new Pen(Color.Black), new Rectangle(r.X, yStart, r.Width, y - yStart));
            return y - yStart;
        }

        private int PrintHeader(PrintPageEventArgs e, int yStart)
        {
            var r = GetPrintArea(e);
            var y = yStart;
            var g = e.Graphics;
            y += PrintStyledLine(" ", "DETAILS", g, r.X, y);
            y += PrintStyledLine("INVOICE", "H1", g, r.X, y);
            y += PrintStyledLine("Invoice number: \xb" + invoice["SeriesNumber"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Invoicing date: \xb" + invoice["CreationDate"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Delivery date: \xb" + invoice["DeliveryDate"], "DETAILS", g, r.X, y);
            y += PrintStyledLine("Payment expires: \xb" + invoice["PaymentExpiration"], "DETAILS", g, r.X, y);
            return y - yStart;
        }

        private static int PrintRule(PrintPageEventArgs e, int y)
        {
            y += 2;
            var r = GetPrintArea(e);
            e.Graphics.DrawLine(new Pen(Color.Blue), r.X, y, r.X + r.Width, y);
            return 5;
        }

        private static int PrintStyledLine(string s, string style, Graphics g, int x, int y)
        {
            Font font;
            Brush brush;
            switch (style)
            {
                case "H1":
                    font = new Font("Times New Roman", 12F);
                    brush = new SolidBrush(Color.Blue);
                    break;
                case "SELLER":
                    font = new Font("Liberation Serif", 18F);
                    brush = new SolidBrush(Color.Blue);
                    break;
                default:
                    font = new Font("Times New Roman", 8);
                    brush = new SolidBrush(Color.Black);
                    break;
            }
            var lines = s.Split(new[] { "\xb" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
            {
                if (line != lines[0])
                {

                }
            }
            g.DrawString(s, font, brush, x, y);
            return font.Height * s.Split('\n').Length;
        }

        private static Rectangle GetPrintArea(PrintPageEventArgs e)
        {
            return new Rectangle(
                e.MarginBounds.Left,
                e.MarginBounds.Top,
                e.MarginBounds.Right - e.MarginBounds.Left,
                e.PageBounds.Height - e.MarginBounds.Top
                );
        }

        private void btnPrint_Click(object sender, EventArgs e)
        {
            printDialog.Document = pdInvoice;
            if (printDialog.ShowDialog() == DialogResult.OK)
            {
                pdInvoice.PrinterSettings = printDialog.PrinterSettings;
                pdInvoice.DefaultPageSettings.Margins = margins;
                pdInvoice.Print();
            }
        }

        private void btnZoomIn_Click(object sender, EventArgs e)
        {
            printPreview.Zoom += 0.1;
        }

        private void btnZoomOut_Click(object sender, EventArgs e)
        {
            printPreview.Zoom -= 0.1;
        }

        private void btnOriginal_Click(object sender, EventArgs e)
        {
            printPreview.Zoom = 1.0;
        }

        private void btnOrientation_Click(object sender, EventArgs e)
        {
            LandscapeOrientation = !LandscapeOrientation;
            printPreview.InvalidatePreview();
        }
    }
}